package vip.xiaonuo.smzq.modular.nbfb.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.Includes;
import com.deepoove.poi.data.style.ParagraphStyle;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.xwpf.NiceXWPFDocument;
import com.deepoove.poi.xwpf.XWPFSection;
import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.geotools.ows.ServiceException;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.smzq.core.tools.file.PathUtils;
import vip.xiaonuo.smzq.modular.dictionary.entity.Cb01Zh;
import vip.xiaonuo.smzq.modular.dictionary.mapper.Cb01ZhMapper;
import vip.xiaonuo.smzq.modular.kcl.entity.Clbdcjl;
import vip.xiaonuo.smzq.modular.kcl.entity.Wjglb;
import vip.xiaonuo.smzq.modular.kcl.entity.Zycl;
import vip.xiaonuo.smzq.modular.kcl.mapper.ClbdcjlMapper;
import vip.xiaonuo.smzq.modular.kcl.mapper.WjglbMapper;
import vip.xiaonuo.smzq.modular.nbfb.entity.param.KszyclParam;
import vip.xiaonuo.smzq.modular.nbfb.entity.vo.ClbmlVo;
import vip.xiaonuo.smzq.modular.nbfb.entity.vo.KczyVo;
import vip.xiaonuo.smzq.modular.nbfb.mapper.KszyclMapper;
import vip.xiaonuo.smzq.modular.nbfb.service.IKszyclService;
import vip.xiaonuo.smzq.modular.user.entity.User;
import vip.xiaonuo.smzq.modular.user.mapper.UserMapper;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class KszyclServiceImpl extends ServiceImpl<KszyclMapper, Zycl> implements IKszyclService {

    //矿山资源储量
    @Resource
    KszyclMapper kszyclMapper;

    //用户
    @Resource
    private UserMapper userMapper;

    //储量表导出记录
    @Resource
    private ClbdcjlMapper clbdcjlMapper;

    //文件上传关联
    @Resource
    private WjglbMapper wjglbMapper;

    //字典综合1
    @Resource
    private Cb01ZhMapper cb01ZhMapper;


    // 正则说明：
    // - 匹配以字母开头的单位片段，后面跟着数字2或3，且不被字母包围（排除化学式）
    // - 如：cm2 → 替换； H2O → 不匹配
    private static final Pattern UNIT_EXP_PATTERN = Pattern.compile(
            "\\b([a-zA-Z]{1,4})([23])\\b(?!([a-zA-Z]))", Pattern.CASE_INSENSITIVE);

    // 上标映射表
    private static final java.util.Map<Character, Character> SUPERSCRIPT_MAP = new java.util.HashMap<>();

    static {
        SUPERSCRIPT_MAP.put('2', '\u00B2'); // ²
        SUPERSCRIPT_MAP.put('3', '\u00B3'); // ³
    }

    //矿体情况
    private static final int KTQK = 20;
    //利用情况
    private static final int LYQK = 24;
    //综合回收
    private static final int ZHHS = 20;
    //报告
    private static final int BG = 25;

    //文件临时路径
    @Value("${file.base}")
    String baseFilePath;

    /**
     * 查询矿山资源储量表
     *
     * @param param
     * @author sqq
     * @since 2024/8/20
     */
    @Override
    public List<KczyVo> getKczyVoList(KszyclParam param) {

        //行政区代码级联多选
        List<String> xzqhList = new ArrayList<>();
        if (param.getXzqh() != null) {
            xzqhList = Arrays.stream(param.getXzqh().split(","))
                    .map(String::trim) // 去除前后空白
                    .collect(Collectors.toList());


        } else {
            throw new CommonException("请选择行政区代码");
        }
        List<KczyVo> list = new ArrayList<>();
        //判断是否是超频铁矿（读取数据库模式不同）
        if (param.getKcdl() == 701000) {
//            String pgsql=getXzqSqlcp(param.getXzqh());
//            list = kszyclMapper.getKczyVoListUltraPoor(param.getNd(), pgsql);
            list = kszyclMapper.getKczyVoListUltraPoor(param.getNd(), xzqhList);
        } else {
            list = kszyclMapper.getKczyVoList(param.getNd(), xzqhList, param.getKcdl());
        }

        for (KczyVo item : list) {
            handleZeroFields(item);
        }

        return list;
    }

    /**
     * 获取储量表导出目录信息
     *
     * @param param
     * @author sqq
     * @since 2024/8/20
     */
    public List<ClbmlVo> getClbmlVoList(KszyclParam param) {


        //行政区代码级联多选
        List<String> xzqhList = new ArrayList<>();
        if (param.getXzqh() != null) {
            xzqhList = Arrays.stream(param.getXzqh().split(","))
                    .map(String::trim) // 去除前后空白
                    .collect(Collectors.toList());


        } else {
            throw new CommonException("请选择行政区代码");
        }

        List<ClbmlVo> list = new ArrayList<>();

        if (param.getKcdl() == 701000) {
//            String pgsql=getXzqSqlcp(param.getXzqh());
//            list = kszyclMapper.getClbmlVoListUltraPoor(param.getNd(), pgsql);
            list = kszyclMapper.getClbmlVoListUltraPoor(param.getNd(), xzqhList);
        } else {
            // sql = sql + " and zh.dm/10*100 =" + param.getKcdl();
            list = kszyclMapper.getClbmlVoList(param.getNd(), xzqhList, param.getKcdl());

        }

        return list;
    }


    /**
     * 整理储量表导出目录
     */
    public List<Map<String, Object>> getClbmlVoMapList(List<ClbmlVo> list) {

        return list.stream()
                .collect(Collectors.groupingBy(ClbmlVo::getFlzd))
                .entrySet().stream()
                .map(entry -> {
                    Map<String, Object> map = new HashMap<>();
                    map.put("list", entry.getValue());
                    ClbmlVo firstItem = entry.getValue().get(0);
                    map.put("kcmc", firstItem.getKcmc());
                    map.put("num", firstItem.getPageNum());
                    map.put("flzd", firstItem.getFlzd());
                    return map;
                })
                .sorted(Comparator.comparing(map -> (String) map.get("flzd") != null ? (String) map.get("flzd") : "")) // 按 flzd 字符串升序排序
                .collect(Collectors.toList());
    }


    /**
     * 整理数据样式
     *
     * @param item
     */
    private void handleZeroFields(KczyVo item) {
        if (item.getDw() != null && !item.getDw().equals("")) {
            item.setDw(replaceChemicalFormulasWithSubscripts(item.getDw()).replaceAll("\n+$", ""));
        }
        // 初始标志位，假设所有值都是 "0.00"
        // boolean allZero = true;

        String[] kctjdx = item.getFlzd().split("_");


        if (item.getDw() != null && item.getDw().contains("/")) {
            if ("0.00".equals(item.getByjsl())) {
                item.setByjsl("0.00".equals(item.getByksl()) ? "" : "0.00");
            }
            if ("0.00".equals(item.getLjcmjsl())) {
                item.setLjcmjsl("0.00".equals(item.getLicmksl()) ? "" : "0.00");
            }
            if ("0.00".equals(item.getKcljsl())) {
                item.setKcljsl("0.00".equals(item.getKclksl()) ? "" : "0.00");
            }
            if ("0.00".equals(item.getSsljsl())) {
                item.setSsljsl("0.00".equals(item.getSslksl()) ? "" : "0.00");
            }
            if ("0.00".equals(item.getKczjjsl())) {
                item.setKczjjsl("0.00".equals(item.getKczjksl()) ? "" : "0.00");
            }
            if ("0.00".equals(item.getCszjjsl())) {
                item.setCszjjsl("0.00".equals(item.getCszjksl()) ? "" : "0.00");
            }
            if ("0.00".equals(item.getSpyfjsl())) {
                item.setSpyfjsl("0.00".equals(item.getSpyfksl()) ? "" : "0.00");
            }
            if ("0.00".equals(item.getQtjsl())) {
                item.setQtjsl("0.00".equals(item.getQtksl()) ? "" : "0.00");
            }

        }

        if (item.getKcmc().contains("石榴子石") || item.getKcmc().contains("长石") || !item.getDw().contains("/")) {
            item.setByjsl("");
            item.setLjcmjsl("");
            item.setKcljsl("");
            item.setSsljsl("");
            item.setKczjjsl("");
            item.setCszjjsl("");
            item.setSpyfjsl("");
            item.setQtjsl("");

        }

        if ("0.00".equals(item.getByksl())) item.setByksl("");
        if ("0.00".equals(item.getLicmksl())) item.setLicmksl("");
        if ("0.00".equals(item.getKclksl())) item.setKclksl("");
        if ("0.00".equals(item.getSslksl())) item.setSslksl("");
        if ("0.00".equals(item.getKczjksl())) item.setKczjksl("");
        if ("0.00".equals(item.getCszjksl())) item.setCszjksl("");
        if ("0.00".equals(item.getSpyfksl())) item.setSpyfksl("");
        if ("0.00".equals(item.getQtksl())) item.setQtksl("");


        //利用情况
        getKczyVoLyqk(item);

        //报告
        getKczyVoBg(item);

        //综合回收
        getKczyVoZhhs(item);

        //矿产信息
        if (item.getKcxx() != null && !item.getKcxx().equals("")) {
            item.setKcxx(formattedString(replaceNewlineCharacters(item.getKcxx())));
        }
        if (item.getKcxxb() != null && !item.getKcxxb().equals("")) {
            item.setKcxxb(formattedString(replaceNewlineCharacters(item.getKcxxb())));
        }
        if (item.getKcxxc() != null && !item.getKcxxc().equals("")) {
            item.setKcxxc(formattedString(replaceNewlineCharacters(item.getKcxxc())));
        }

        //矿产矿体情况
        getKczyVo(item);


        //矿产品级
        getKczyVoKcpj(item);

        if (item.getNum().equals("0")) {
            item.setNum(" ");
        }

    }


    public void getKczyVo(KczyVo item) {
        List<String> list = new ArrayList<>();


        // 矿体情况
        addProcessedValueToList(item.getKtqk(), list, KTQK);
        item.setKtqk("");
        addProcessedValueToList(item.getKtqkb(), list, KTQK);
        item.setKtqkb("");
        addProcessedValueToList(item.getKtqkc(), list, KTQK);
        item.setKtqkc("");
        addProcessedValueToList(item.getKtqkd(), list, KTQK);
        item.setKtqkd("");
        addProcessedValueToList(item.getKtqke(), list, KTQK);
        item.setKtqke("");

        // 设置字段值
        if (list.size() > 0) item.setKtqk(list.get(0));
        if (list.size() > 1) item.setKtqkb(list.get(1));
        if (list.size() > 2) item.setKtqkc(list.get(2));
        if (list.size() > 3) item.setKtqkd(list.get(3));
        if (list.size() > 4) item.setKtqke(list.get(4));

    }

    public void getKczyVoLyqk(KczyVo item) {
        List<String> list = new ArrayList<>();

        // 利用情况
        addProcessedValueToList(item.getLyqk(), list, LYQK);
        item.setLyqk("");
        addProcessedValueToList(item.getLyqkb(), list, LYQK);
        item.setLyqkb("");
        addProcessedValueToList(item.getLyqkc(), list, LYQK);
        item.setLyqkc("");
        addProcessedValueToList(item.getLyqkd(), list, LYQK);
        item.setLyqkd("");
        addProcessedValueToList(item.getLyqke(), list, LYQK);
        item.setLyqke("");
        addProcessedValueToList(item.getLyqkf(), list, LYQK);
        item.setLyqkf("");

        // 设置字段值
        if (list.size() > 0) item.setLyqk(list.get(0));
        if (list.size() > 1) item.setLyqkb(list.get(1));
        if (list.size() > 2) item.setLyqkc(list.get(2));
        if (list.size() > 3) item.setLyqkd(list.get(3));
        if (list.size() > 4) item.setLyqke(list.get(4));
        if (list.size() > 5) item.setLyqkf(list.get(5));

    }

    public void getKczyVoBg(KczyVo item) {
        List<String> list = new ArrayList<>();
        if (item.getBg().contains("\n")) {
            item.setBg(item.getBg().replace("\n", ""));
        }
        if (item.getBgb().contains("\n")) {
            item.setBgb(item.getBgb().replace("\n", ""));
        }
        if (item.getBgc().contains("\n")) {
            item.setBgc(item.getBgc().replace("\n", ""));
        }
        if (item.getBgd().contains("\n")) {
            item.setBgd(item.getBgd().replace("\n", ""));
        }
        // 矿体情况
        addProcessedValueToList(item.getBg(), list, BG);
        item.setBg("");
        addProcessedValueToList(item.getBgb(), list, BG);
        item.setBgb("");
        addProcessedValueToList(item.getBgc(), list, BG);
        item.setBgc("");
        addProcessedValueToList(item.getBgd(), list, BG);
        item.setBgd("");


        // 设置字段值
        if (list.size() > 0) item.setBg(list.get(0));
        if (list.size() > 1) item.setBgb(list.get(1));
        if (list.size() > 2) item.setBgc(list.get(2));
        if (list.size() > 3) item.setBgd(list.get(3));


    }

    public void getKczyVoZhhs(KczyVo item) {
        List<String> list = new ArrayList<>();

        // 矿体情况
        addProcessedValueToList2(item.getZhhs(), list, ZHHS);
        item.setZhhs("");
        addProcessedValueToList2(item.getZhhsb(), list, ZHHS);
        item.setZhhsb("");
        addProcessedValueToList2(item.getZhhsc(), list, ZHHS);
        item.setZhhsc("");


        // 设置字段值
        if (list.size() > 0) item.setZhhs(list.get(0));
        if (list.size() > 1) item.setZhhsb(list.get(1));
        if (list.size() > 2) item.setZhhsc(list.get(2));


    }

    public void getKczyVoKcpj(KczyVo item) {
        List<String> list = new ArrayList<>();

        // 矿体情况
        addProcessedValueToList3(item.getKcpj(), list);
        item.setKcpj("");
        addProcessedValueToList3(item.getKcpjb(), list);
        item.setKcpjb("");


        // 设置字段值
        if (list.size() > 0) item.setKcpj(list.get(0));
        if (list.size() > 1) item.setKcpjb(list.get(1));


    }

    private void addProcessedValueToList(String value, List<String> list, int bytes) {
        if (value != null && !value.equals("")) {
            try {
                String processedValue = formattedString(processAndConcatenateStrings(replaceNewlineCharacters(value), bytes));
                list.add(processedValue);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void addProcessedValueToList2(String value, List<String> list, int bytes) {
        if (value != null && !value.equals("")) {
            try {
                String processedValue = formattedString(processAndConcatenateStrings(replaceNewlineCharacters(value), bytes));
                list.add(processedValue);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void addProcessedValueToList3(String value, List<String> list) {
        if (value != null && !value.equals("")) {
            try {
                String processedValue = formattedString(replaceChemicalFormulasWithSubscripts(replaceNewlineCharacters(value)));
                list.add(processedValue);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 储量表数据整理
     *
     * @param list<KczyVo> list
     * @return List<Map < String, Object>> resultList
     */
    public List<Map<String, Object>> getKszyclVoList(List<KczyVo> list, Integer nd) {

        return list.stream()
                .collect(Collectors.groupingBy(KczyVo::getFlzd))
                .entrySet().stream()
                .map(entry -> {
                    Map<String, Object> map = new HashMap<>();
                    map.put("list", entry.getValue());
                    KczyVo firstItem = entry.getValue().get(0);
                    map.put("kcmc", firstItem.getKcmc());
                    map.put("kqbh", firstItem.getKqbh());
                    map.put("nd", nd);
                    map.put("dw", firstItem.getDw());
                    map.put("flzd", firstItem.getFlzd());
                    return map;
                })
                .sorted(Comparator.comparing(map -> (String) map.get("flzd") != null ? (String) map.get("flzd") : "")) // 按 flzd 字符串升序排序
                .collect(Collectors.toList());
    }


    /**
     * 导出矿产资源储量表
     *
     * @param param
     * @param response
     * @param param    查询参数，此处未在函数体中使用，可能在其他地方有使用
     * @param response HTTP响应对象，用于处理错误时返回状态码
     * @author sqq
     * @since 2024/8/29 9:54
     */
    public void exportKszycl(KszyclParam param, HttpServletResponse response) {
        //设置最小解压比率
        ZipSecureFile.setMinInflateRatio(0.005); // 根据需要调整这个值
        String time = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        SaBaseLoginUser saBaseLoginUser = StpLoginUserUtil.getLoginUser();
        String fbr = saBaseLoginUser.getName();
        try {
            // 非煤炭模版文件路径
            String templateFilePath = PathUtils.getNbfbPath(7);
            // 煤炭模版文件路径
            String coalTemplatePath = PathUtils.getNbfbPath(8);


            // 获取矿产资源数据列表
            List<KczyVo> list = LeftRightAlignment(getKczyVoList(param));

            // 配置模板策略
            LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
            Configure config = Configure.builder().bind("list", policy).build();
            List<Map<String, Object>> dataList = new ArrayList<>();
            dataList = getKszyclVoList(list, param.getNd());

            //最终导出word
            // NiceXWPFDocument finalDoc = new NiceXWPFDocument();

            //创建一个新的Word文档来保存所有内容（内容）
            NiceXWPFDocument finalDocNr = new NiceXWPFDocument();

            List<NiceXWPFDocument> docs = new ArrayList<>();
            Map<String, NiceXWPFDocument> docMap = new HashMap<>();
            List<String> kcmcList = new ArrayList<>();

            // 遍历每个数据集，渲染模板并将结果合并到最终文档中
            for (Map<String, Object> currentData : dataList) {
                String kcmc = (String) currentData.get("kcmc");
                kcmcList.add(kcmc);
                // 根据条件选择模板路径并加载模板
                String selectedTemplatePath = !kcmc.equals("煤炭") ? templateFilePath : coalTemplatePath;
                InputStream templateStream = getClassPathResource(selectedTemplatePath);

                XWPFTemplate template = null;
                // 加载模板
                // XWPFTemplate 编译
                template = XWPFTemplate.compile(templateStream, config);

                // 渲染模板
                template.render(currentData);

                // 将渲染后的模板追加到最终文档中
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                template.write(baos);

                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                NiceXWPFDocument renderedTemplate = new NiceXWPFDocument(bais);
                docMap.put(kcmc, renderedTemplate);
                //docs.add(renderedTemplate);
                // 关闭模板（如果适用）
                ((AutoCloseable) template).close();
                templateStream.close();

            }
            String fileDoc = "";

            //黑色金属单独处理
//            if (param.getKcdl() == 700200 || param.getKcdl() == 700400 || param.getKcdl() == 700800) {
//            if (param.getKcdl() == 700100 || param.getKcdl() == 700400 || param.getKcdl() == 700800) {
            if (param.getKcdl() == 1) {
                List<String> filePaths = new ArrayList<>();
                for (int i = 0; i < kcmcList.size(); i++) {
                    try (NiceXWPFDocument renderedTemplate = docMap.get(kcmcList.get(i))) {
                        String fileDocNext = baseFilePath + File.separator + DateUtil.format(new Date(), "yyyyMMddHHmmss") + "_" + kcmcList.get(i) + ".docx";
                        // 输出到客户端
                        OutputStream out = new FileOutputStream(fileDocNext);
                        // 合并单元格
                        mergeCells(renderedTemplate.getXWPFDocument());
                        //写出文件
                        renderedTemplate.write(out);
                        //整理地址用于压缩zip
                        filePaths.add(fileDocNext);
                        // 注意：这里不重新赋值finalDocNr，因为merge方法应该修改现有的文档对象而不是返回一个新的
                    } catch (IOException e) {
                        throw new RuntimeException("Error merging document at index " + i, e);
                    }
                }
                fileDoc = baseFilePath + File.separator + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".zip";
                //压缩zip
                zipFiles(filePaths, fileDoc);

//                }
            }

            //其他正常处理
            else {

                for (int i = 0; i < kcmcList.size(); i++) {
                    try (NiceXWPFDocument renderedTemplate = docMap.get(kcmcList.get(i))) {
                        if (i > 0) {
                            // 在基础文档末尾插入分页符
                            insertPageBreak(finalDocNr);
                        }
                        // 合并模板
                        finalDocNr = finalDocNr.merge(renderedTemplate);

                        // 注意：这里不重新赋值finalDocNr，因为merge方法应该修改现有的文档对象而不是返回一个新的
                    } catch (IOException e) {
                        throw new RuntimeException("Error merging document at index " + i, e);
                    }
                }
                // 合并单元格
                mergeCells(finalDocNr.getXWPFDocument());


//            //设置纸张大小与方向
                setPaperSize(finalDocNr);

                //创建默认的页脚(该页脚主要只居中显示页码)
                createDefaultFooter(finalDocNr);

                fileDoc = baseFilePath + File.separator + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".docx";
                OutputStream out = new FileOutputStream(fileDoc);
                finalDocNr.write(out);
                // 关闭最终文档
                finalDocNr.close();
            }

            File file = new File(fileDoc);
            // 输出到客户端
            try {

                if (file.exists()) {
                    String fileName = file.getName();
                    Wjglb wjglb = new Wjglb();
                    String suffix = fileName.substring(fileName.lastIndexOf("."));
                    String wjName = fileName.substring(0, fileName.lastIndexOf("."));
                    wjglb.setWjmc(wjName);
                    wjglb.setWjhz(suffix);
                    wjglb.setWjdx(Math.toIntExact(file.length()));
                    wjglb.setWjlj(fileDoc);
                    wjglb.setWjsclj(fileDoc);
                    wjglb.setScsj(time);
                    wjglb.setScr(fbr);
                    wjglbMapper.insert(wjglb);

                    Clbdcjl clbdcjl = new Clbdcjl();
                    clbdcjl.setNd(param.getNd());
                    clbdcjl.setCzrid(saBaseLoginUser.getId());
                    clbdcjl.setCzrxm(fbr);
                    clbdcjl.setWjid(wjglb.getPkid());

                    Cb01Zh cb01Zh = new Cb01Zh();
                    LambdaQueryWrapper<Cb01Zh> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(Cb01Zh::getDm, param.getKcdl());
                    queryWrapper.eq(Cb01Zh::getBh, 770);
                    cb01Zh = cb01ZhMapper.selectOne(queryWrapper);
                    clbdcjl.setDcdl(cb01Zh.getMc());
                    clbdcjl.setDcdbm(param.getKcdl());
                    clbdcjl.setCzsj(time);
                    clbdcjlMapper.insert(clbdcjl);

                    PathUtils.singleFileShow(fileDoc, response);
                }
            } catch (Exception e) {
                // 设置响应状态码为服务器错误
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                e.printStackTrace();
            } finally {
//                if (file.exists()) {
//
//                    FileUtil.del(file);
//                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 加载类路径下的资源文件。
     *
     * @param path 类路径下的资源路径
     * @return 输入流
     * @throws IOException 如果无法加载资源文件
     */
    private static InputStream getClassPathResource(String path) throws IOException {
        ClassPathResource classPathResource1 = new ClassPathResource(path);
        InputStream inputStream = classPathResource1.getStream();
        if (inputStream == null) {
            throw new FileNotFoundException("Resource not found in classpath: " + path);
        }
        return inputStream;
    }

    public void exportKszclMl(KszyclParam param, HttpServletResponse response) {
        // 放宽 ZIP 压缩比率限制
        ZipSecureFile.setMinInflateRatio(0.005); // 关键修复点

        Wjglb wjglb = new Wjglb();
        if (param.getFileId() != null) {
            wjglb = wjglbMapper.selectById(param.getFileId());
        } else {
            throw new CommonException("文件不存在");
        }

        File file = new File(wjglb.getWjlj());

        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        Configure config = Configure.builder().bind("list", policy).build();

        String mlmbTemplatePath = PathUtils.getNbfbPath(9);
        List<ClbmlVo> clbmlVos = wordToPdf(getClbmlVoList(param), file);
        List<Map<String, Object>> clbmlList = getClbmlVoMapList(clbmlVos);

        NiceXWPFDocument finalDocMl = new NiceXWPFDocument();
        try {
            for (Map<String, Object> map : clbmlList) {
                ClassPathResource classPathResource1 = new ClassPathResource(mlmbTemplatePath);
                try (
                        InputStream inputStream = classPathResource1.getStream();
                        XWPFTemplate template = XWPFTemplate.compile(inputStream, config)
                ) {
                    template.render(map);

                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    template.write(baos);
                    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                    NiceXWPFDocument renderedTemplate = new NiceXWPFDocument(bais);

                    finalDocMl = finalDocMl.merge(renderedTemplate);
                }
            }

            setPaperSize(finalDocMl);
            createDefaultFooter(finalDocMl);

            String fileDoc = Paths.get(baseFilePath, DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".docx").toString();

            try (OutputStream out = new FileOutputStream(fileDoc)) {
                finalDocMl.write(out);
                PathUtils.singleFileShow(fileDoc, response);
            }

        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            log.error("导出目录失败", e); // 如果有日志框架
            throw new RuntimeException("导出目录失败", e);
        } finally {
            try {
                finalDocMl.close();
            } catch (IOException e) {
                System.out.println("关闭文件输出流时出错：" + e.getMessage());
            }

            String fileDoc = Paths.get(baseFilePath, DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".docx").toString();
            File fileML = new File(fileDoc);
            if (fileML.exists()) {
                FileUtil.del(fileML);
            }
        }
    }

    //通过计算补充空表格
    public List<KczyVo> LeftRightAlignment(List<KczyVo> list) {

        //反回的结果列表
        List<KczyVo> resultList = new ArrayList<>();

        // 根据指定字段分组并保持原始顺序
        Map<String, List<KczyVo>> groupedMap = list.stream()
                .collect(Collectors.groupingBy(
                        KszyclServiceImpl::createGroupKey, // 创建分组键
                        LinkedHashMap::new, // 使用 LinkedHashMap 保持插入顺序
                        Collectors.toList() // 收集到列表中
                ));

        // 将 map 的值转换为 list of lists
        List<List<KczyVo>> result = new ArrayList<>(groupedMap.values());
        for (List<KczyVo> subList : result) {

            //最后的拼接好字段用于计算出行数的
            String kcxx = "";
            String kcpj = "";
            String kcbg = "";
            String lyqk = "";
            String zhhs = "";
            String ktqk = "";


            //拼接字段
            for (KczyVo kczyVo : subList) {

                // 拼接字段并处理 null 或 ""
                String kcxxnew = safeConcat(kczyVo.getKcxx(), kczyVo.getKcxxb(), kczyVo.getKcxxc());
                String kcpjnew = safeConcat(kczyVo.getKcpj(), kczyVo.getKcpjb());
                String kcbgnew = safeConcat(kczyVo.getBg(), kczyVo.getBgb(), kczyVo.getBgc(), kczyVo.getBgd());
                String lyqknew = safeConcat(kczyVo.getLyqk(), kczyVo.getLyqkb(), kczyVo.getLyqkc(), kczyVo.getLyqkd(), kczyVo.getLyqke(), kczyVo.getLyqkf());
                String zhhsnew = safeConcat(kczyVo.getZhhs(), kczyVo.getZhhsb(), kczyVo.getZhhsc());
                String ktqknew = safeConcat(kczyVo.getKtqk(), kczyVo.getKtqkb(), kczyVo.getKtqkc(), kczyVo.getKtqkd(), kczyVo.getKtqke());

                if (!kcxx.contains(kcxxnew)) {
                    kcxx = kcxx + kcxxnew;
                }

                if (!kcpj.contains(kcpjnew)) {
                    kcpj = kcpj + kcpjnew;
                }
                if (!kcbg.contains(kcbgnew)) {
                    kcbg = kcbg + kcbgnew;
                }
                if (!ktqk.contains(ktqknew)) {
                    ktqk = ktqk + ktqknew;
                }
                if (!lyqk.contains(lyqknew)) {
                    lyqk = lyqk + lyqknew;
                }
                if (!zhhs.contains(zhhsnew)) {
                    zhhs = zhhs + zhhsnew;
                }
            }


            int kcxxCount = getLineCount(kcxx);
            int kcpjCount = getLineCount(kcpj);
            int kcbgCount = getLineCount(kcbg);
            int lyqkCount = getLineCount(lyqk);
            int zhhsCount = getLineCount(zhhs);
            int ktqkCount = getLineCount(ktqk);

            // 将所有计数值放入列表

// 将所有计数值放入列表，并仅在 count 不为 0 时加上对应的值
            List<Integer> counts = Arrays.asList(
                    kcxxCount != 0 ? kcxxCount + 3 : 0,
                    kcpjCount != 0 ? kcpjCount + 2 : 0,
                    kcbgCount != 0 ? kcbgCount + 4 : 0,
                    lyqkCount != 0 ? lyqkCount + 6 : 0,
                    zhhsCount != 0 ? zhhsCount + 3 : 0,
                    ktqkCount != 0 ? ktqkCount + 5 : 0
            );
            // 使用 Collections.max() 找出最大值
            int maxCount = (Collections.max(counts));

            int difference = maxCount - subList.size();

            if (difference > 0) {
                int count = getIntervalMaxValue(difference);
                for (int i = 0; i < count - 2; i++) {
                    KczyVo kczyVoold = (subList.get(subList.size() - 1));
                    KczyVo kczyVo = new KczyVo();
                    BeanUtil.copyProperties(kczyVoold, kczyVo);
                    kczyVo.setZylx(null);
                    kczyVo.setByjsl(null);
                    kczyVo.setByksl(null);
                    kczyVo.setLjcmjsl(null);
                    kczyVo.setLicmksl(null);
                    kczyVo.setSsljsl(null);
                    kczyVo.setSslksl(null);
                    kczyVo.setKcljsl(null);
                    kczyVo.setKclksl(null);
                    kczyVo.setKczjjsl(null);
                    kczyVo.setKczjksl(null);
                    kczyVo.setCszjjsl(null);
                    kczyVo.setCszjksl(null);
                    kczyVo.setSpyfjsl(null);
                    kczyVo.setSpyfksl(null);
                    kczyVo.setQtjsl(null);
                    kczyVo.setQtksl(null);
                    subList.add(kczyVo);
                }
            }
//for (KczyVo kczyVo : subList){
//    System.out.println(kczyVo);
//}

            resultList.addAll(subList);

        }


        return resultList;
    }


    private static String safeConcat(String... parts) {
        StringBuilder sb = new StringBuilder();
        for (String part : parts) {
            if (part == null || part.isEmpty()) {
                sb.append("\n");
            } else {
                sb.append(part);
            }
        }
        return sb.toString();
    }


    /**
     * 根据给定规则返回输入整数所在区间的最大值
     *
     * @param num 输入的正整数
     * @return 所在区间的最大值
     */
    public static int getIntervalMaxValue(int num) {
        if (num < 1) {
            num = 1;
        }

        // 计算所在区间
        int interval = ((num - 1) / 4) + 1;
        // 计算该区间的最大值

        return interval * 3;
    }

    /**
     * 根据指定字段进行分组
     *
     * @param vo
     * @return
     */
    private static String createGroupKey(KczyVo vo) {
        return String.join(":",
                Optional.ofNullable(vo.getKcdl()).orElse(""),
                Optional.ofNullable(vo.getKqmc()).orElse(""),
                Optional.ofNullable(vo.getKqbh()).orElse(""),
                Optional.ofNullable(vo.getKcmc()).orElse(""),
                Optional.ofNullable(vo.getNd()).orElse(""),
                Optional.ofNullable(vo.getDw()).orElse(""),
                Optional.ofNullable(vo.getKcxx()).orElse(""),
                Optional.ofNullable(vo.getNum()).orElse(""));
    }


    /**
     * 获取字符串的行数
     */

    public int getLineCount(String input) {

        if (input == null || input.equals("")) {
            return 0;
        }

        String[] parts = input.split("\n");
        return parts.length;
    }


    /**
     * 在文档末尾插入分页符。
     *
     * @param document 文档对象
     */
    private static void insertPageBreak(XWPFDocument document) {
        XWPFParagraph paragraph = document.createParagraph();
        XWPFRun run = paragraph.createRun();

        // 创建分页符
        run.addBreak(BreakType.PAGE);
    }


    /**
     * 将化学公式中的数字转换为下标形式。
     *
     * @param input 输入的化学公式字符串
     * @return 转换后的字符串
     */
    public String replaceChemicalFormulasWithSubscripts(String input) {
        // 改进后的正则表达式匹配化学公式中的元素及其后的数字
        Pattern pattern = Pattern.compile("(\\b[a-zA-Z]+[a-z]*)([0-9]+)|([a-zA-Z]+[a-z]*)([0-9]+)");

        String strInput = "";
        String[] strs = input.split("\n");
        for (String str : strs) {
            //   * 匹配以字母开头的单位片段，后面跟着数字2或3，且不被字母包围（排除化学式）* 如：cm2 → 替换； H2O → 不匹配
            if (str.contains("：") || str.contains(":")) {
                str = str.replace('：', ':');
                String[] parts = str.split(":", 2);
                str = parts[0] + ":" + formatUnit(parts[1]);
            }
            strInput = strInput + str + "\n";
        }
        input = strInput;
        Matcher matcher = pattern.matcher(input);

        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            // 获取元素部分
            String elementPart = matcher.group(1) != null ? matcher.group(1) : matcher.group(3);
            // 获取数字部分
            String numberPart = matcher.group(2) != null ? matcher.group(2) : matcher.group(4);
            // 将数字部分转换为下标形式，并拼接回元素部分
            String replacement = elementPart + convertToSubscript(numberPart);
            // 替换匹配到的部分
            matcher.appendReplacement(result, replacement);
        }
        // 添加未匹配的部分
        matcher.appendTail(result);

        return result.toString();
    }


    public static void main(String[] args) {
        String unit = "g/cm\u00B2";
        System.out.println(unit);

        //System.out.println(replaceChemicalFormulasWithSubscripts2(a));
    }


    /**
     * 将数字字符串转换为下标形式。
     *
     * @param numberPart 数字字符串
     * @return 转换后的下标字符串
     */
    private String convertToSubscript(String numberPart) {
        StringBuilder sb = new StringBuilder();
        for (char c : numberPart.toCharArray()) {
            if (Character.isDigit(c)) {
                // 如果是数字，则转换为下标形式
                sb.append(getSubscriptChar(c));
            } else {
                // 如果不是数字，则保留原字符
                sb.append(c);
            }
        }
        return sb.toString();
    }


    /**
     * 获取指定字符对应的下标字符。
     *
     * @param c 字符
     * @return 下标字符
     */
    private char getSubscriptChar(char c) {
        switch (c) {
            case '0':
                return '₀';
            case '1':
                return '₁';
            case '2':
                return '₂';
            case '3':
                return '₃';
            case '4':
                return '₄';
            case '5':
                return '₅';
            case '6':
                return '₆';
            case '7':
                return '₇';
            case '8':
                return '₈';
            case '9':
                return '₉';
            default:
                return c; // 如果不是数字，则返回原字符
        }
    }

    /**
     * 对输入的多行字符串进行处理，如果某行不是以"(数字)"形式开头，
     * 则在其前添加3个空格。如果某行以数字开头且紧跟在编号行之后，
     * 则该行不添加额外空格。
     *
     * @param input 多行字符串
     * @return 格式化后的字符串
     */
    public static String formattedString(String input) {
        if (input == null || input.isEmpty()) {
            return input;
        }
        if (input.endsWith("\n")) {
            return input.substring(0, input.length() - 1);
        }
        return input;
    }


    /**
     * 替换字符串中的换行符为换行符。
     *
     * @param str 输入的字符串
     * @return 替换后的字符串
     */
    public static String replaceNewlineCharacters(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }

        // 检查字符串是否以分号结尾
        boolean endsWithSemicolon = str.endsWith("；") || str.endsWith(";");
        // 如果字符串以分号结尾，移除最后一个换行符
        if (endsWithSemicolon) {
            str = str.substring(0, str.length() - 1);
        }
        // 替换分号为换行符
        str = str.replace(" ", "");
        //str = str.replace("；", "\n");
        str = str.replace(";", "\n");
        //str = str.replace("||", "\n||");


        return str;

    }


    /**
     * 处理字符串，将字符串中的换行符替换为制表符。
     *
     * @param input
     * @return
     */
    public static String processAndConcatenateStrings(String input, int bytesold) {

        StringBuilder finalResult = new StringBuilder();
        String[] parts = input.split("\n");

        for (String part : parts) {
            int bytes = bytesold;
            if (part.contains(":") || part.contains("：")) {
                part = part.replace("：", ":");
                String[] splitByColon = part.split(":", 2);
                if (splitByColon.length < 2) {
                    // 如果没有冒号后的内容，则保留原样
                    finalResult.append(part).append("\n");
                    continue;
                } else {
                    if (part.contains("(") && part.contains(")")) {
                        bytes = bytes - 2;
                    } else {
                        bytes = bytes - 2;
                    }
                }
                //冒号左
                String leftPart = splitByColon[0] + ":";
                //冒号中
                String intermediate = splitByColon[1];

                //添加下划线处理后数据
                String underlinedNumber = "";
                //未处理的冒号左数据
                String intermediate2 = "";
                //"||"右
                String rightPart = "";

                //判断冒号中是否有“||”若有则是下划线处理标识
                int pipeIndex = intermediate.indexOf("||");
                // 如果存在"||"，则处理下划线
                if (pipeIndex != -1) {
                    //"||"左 获取数据
                    intermediate2 = intermediate.substring(0, pipeIndex);
                    // 添加下划线
                    underlinedNumber = addCombiningSubscript(intermediate2);
                    //"||"右 获取数据 并添加制表符
                    rightPart = intermediate.substring(pipeIndex + 2);
                    int denominator = bytes - getWidth(rightPart);
                    String denominatorstr = "";
                    if (denominator > 0) {
                        for (int i = 1; i < denominator; i++) {
                            denominatorstr += "\u0020";
                        }
                    }
                    rightPart = denominatorstr + rightPart;
                    if (leftPart.contains("(") && leftPart.contains(")")) {
                        bytes = bytes + 1;
                    }

                } else {
                    underlinedNumber = splitByColon[1];
                    intermediate2 = splitByColon[1];
                }


                // 计算左半部分和右半部分的字节数
                int leftPartWidth = getWidth(leftPart);
                int rightPartWidth = getWidth(intermediate2);

                // 计算需要补充的空格数量
                int spacesToAdd = bytes - leftPartWidth - rightPartWidth;
                if (spacesToAdd < 0) {
                    leftPart += "\n\t";
                    spacesToAdd = 0;
                }


                String t = "";
                if (spacesToAdd > 0) {
                    for (int i = 1; i < spacesToAdd; i++) {
                        t += "\u0020";
                    }
                }
                // 构建最终行
                finalResult.append(leftPart)
                        .append(t) // 添加必要的空格
                        .append(underlinedNumber)
                        .append("\n");


                if (!rightPart.equals("")) {
                    finalResult.append(rightPart)
                            .append("\n");
                }

            } else {
                // 处理没有冒号的情况
                finalResult.append(part).append("\n");
            }
        }

        // 移除最后多余的换行符
        if (finalResult.length() > 0) {
            finalResult.setLength(finalResult.length() - 1);
        }

        return finalResult.toString();
    }


    // 辅助函数：计算字符串宽度，假设英文字符占1个单位宽度，中文字符占2个单位宽度
    private static int getWidth(String str) {
        int width = 0;
        for (char c : str.toCharArray()) {
            // 判断是否为中文字符（包括中文标点符号）
            if ((c >= '\u4e00' && c <= '\u9fff') ||
                    (c >= '\u3000' && c <= '\u303f') || // CJK符号和标点
                    (c >= '\ufe30' && c <= '\ufe4f') || // CJK兼容形式
                    (c >= '\uff00' && c <= '\uffef')) { // 半宽和全角形式
                // 中文字符或中文标点符号
                width += 2;
            } else if (Character.isDigit(c) || Character.isLetter(c)) {
                // 数字或字母
                width += 1;
            } else if (Character.getType(c) == Character.CONNECTOR_PUNCTUATION ||
                    Character.getType(c) == Character.DASH_PUNCTUATION ||
                    Character.getType(c) == Character.END_PUNCTUATION ||
                    Character.getType(c) == Character.FINAL_QUOTE_PUNCTUATION ||
                    Character.getType(c) == Character.INITIAL_QUOTE_PUNCTUATION ||
                    Character.getType(c) == Character.OTHER_PUNCTUATION ||
                    Character.getType(c) == Character.START_PUNCTUATION) {
                // 标点符号
                width += 1;
            } else {
                // 其他字符默认处理为1个单位宽度
                width += 1;
            }
        }
        return width;
    }


    /**
     * 将数字转换为下划线形式
     *
     * @param number
     * @return 转换后的下标字符串
     */
    private static String addCombiningSubscript(String number) {
        StringBuilder builder = new StringBuilder();
        for (char c : number.toCharArray()) {
            builder.append(c).append("\u0332");
        }
        return builder.toString();
    }


    /**
     * 合并表格单元格
     *
     * @param document Word文档对象
     */
    private static void mergeCells(XWPFDocument document) {
        // 获取表格列表
        List<XWPFTable> tables = document.getTables();
        if (tables.isEmpty()) {
            return;
        }


        for (int j = 0; j < tables.size(); j++) {
            // 处理第一个表格
            XWPFTable table = tables.get(j);
            // 获取行数
            int numRows = table.getNumberOfRows();

            for (int i = 4; i < numRows; i++) {
                XWPFTableRow row = table.getRow(i);
                for (int columnIndex : new int[]{0, 1, 2, 12, 13, 14, 15}) {
                    mergeCell(table, row, i, columnIndex);

                }
            }
        }
    }

    /**
     * 合并指定列的单元格
     *
     * @param table       表格对象
     * @param row         当前行对象
     * @param rowIndex    当前行索引
     * @param columnIndex 列索引
     */
    private static void mergeCell(XWPFTable table, XWPFTableRow row, int rowIndex, int columnIndex) {
        // 获取当前表格的所有行数
        int rowCount = table.getRows().size();

        // 获取当前行的所有单元格
        List<XWPFTableCell> cells = row.getTableCells();
        if (cells.size() <= columnIndex) {
            return;
        }

        // 只对指定列进行合并
        String columnsToMerge = "0-1-2-12-13-14-15-";
        if (!columnsToMerge.contains(columnIndex + "-")) {
            return;
        }

        // 获取当前列的单元格
        XWPFTableCell cell = cells.get(columnIndex);
        String cellText = cell.getText();

        // 获取当前行的第一列单元格
        XWPFTableCell firstCell = cells.get(0);
        String firstCellText = firstCell.getText();

        // 获取当前单元格的CTTcPr
        CTTcPr tcPr = cell.getCTTc().getTcPr();
        if (tcPr == null) {
            tcPr = cell.getCTTc().addNewTcPr();
        }

        // 特殊处理前几行
        if (rowIndex <= 4) {
            tcPr.addNewVMerge().setVal(STMerge.RESTART);
            System.out.println("Set VMerge.RESTART for column " + columnIndex + " in row " + rowIndex);
            return;
        }

        // 获取上一行
        XWPFTableRow prevRow = table.getRow(rowIndex - 1);
        if (prevRow == null) {
            return;
        }

        // 获取上一行的所有单元格
        List<XWPFTableCell> prevCells = prevRow.getTableCells();
        if (prevCells.size() <= columnIndex) {
            return;
        }

        // 获取上一行的当前列单元格
        XWPFTableCell prevCell = prevCells.get(columnIndex);
        String prevCellText = prevCell.getText();

        // 获取上一行的第一列单元格
        XWPFTableCell prevFirstCell = prevCells.get(0);
        String prevFirstCellText = prevFirstCell.getText();

        // 获取上一行单元格的CTTcPr
        CTTcPr prevTcPr = prevCell.getCTTc().getTcPr();
        if (prevTcPr == null) {
            prevTcPr = prevCell.getCTTc().addNewTcPr();
        }

        // 判断第一列的当前行和上一行数据是否相同
        if (firstCellText.equals(prevFirstCellText)) {
            // 判断当前列的当前行和上一行数据是否相同
            if (cellText.equals(prevCellText)) {
                tcPr.addNewVMerge().setVal(STMerge.CONTINUE);
                System.out.println("Set VMerge.CONTINUE for column " + columnIndex + " in row " + rowIndex);
            } else {
                tcPr.addNewVMerge().setVal(STMerge.RESTART);
                System.out.println("Set VMerge.RESTART for column " + columnIndex + " in row " + rowIndex);
            }
        } else {
            tcPr.addNewVMerge().setVal(STMerge.RESTART);
            System.out.println("Set VMerge.RESTART for column " + columnIndex + " in row " + rowIndex);
        }
    }

    public static void setPaperSize(XWPFDocument document) {
        CTBody body = document.getDocument().getBody();
        CTSectPr sectPr = body.isSetSectPr() ? body.getSectPr() : body.addNewSectPr();
        CTPageSz pageSize = sectPr.addNewPgSz();

        // 设置纸张大小为A3

        pageSize.setW(BigInteger.valueOf(23800)); // A3宽度
        pageSize.setH(BigInteger.valueOf(16840)); // A3高度

        // 设置纸张方向为横向
        pageSize.setOrient(STPageOrientation.LANDSCAPE);
    }


    /**
     * 创建默认的页脚(该页脚主要只居中显示页码)
     *
     * @param docx XWPFDocument文档对象
     * @return 返回文档帮助类对象，可用于方法链调用
     * @throws XmlException XML异常
     * @throws IOException  IO异常
     */
    public static void createDefaultFooter(XWPFDocument docx) {
        // 创建页脚段落
        CTP pageNo = CTP.Factory.newInstance();
        XWPFParagraph footer = new XWPFParagraph(pageNo, docx);
        CTPPr begin = pageNo.addNewPPr();
        begin.addNewPStyle().setVal("系统页脚");
        begin.addNewJc().setVal(STJc.CENTER);
        pageNo.addNewR().addNewFldChar().setFldCharType(STFldCharType.BEGIN);
        pageNo.addNewR().addNewInstrText().setStringValue("PAGE   \\* MERGEFORMAT");

        pageNo.addNewR().addNewFldChar().setFldCharType(STFldCharType.SEPARATE);
        CTR end = pageNo.addNewR();
        CTRPr endRPr = end.addNewRPr();
        endRPr.addNewNoProof();
        endRPr.addNewLang().setVal("en-US");
        end.addNewFldChar().setFldCharType(STFldCharType.END);

        // 获取或创建 CTSectPr 对象
        CTSectPr sectPr = docx.getDocument().getBody().isSetSectPr() ?
                docx.getDocument().getBody().getSectPr() :
                docx.getDocument().getBody().addNewSectPr();

        // 创建页脚策略
        XWPFHeaderFooterPolicy policy = new XWPFHeaderFooterPolicy(docx, sectPr);
        policy.createFooter(STHdrFtr.DEFAULT, new XWPFParagraph[]{footer});
    }

    private final IConverter converter = LocalConverter.builder().build();


    /**
     * 将word文件转换pdf并获取其目录内容
     *
     * @param file Word文档文件
     * @return PDF字节数组
     * @throws Exception 如果转换失败
     */
    public List<ClbmlVo> wordToPdf(List<ClbmlVo> clbmlVoList, File file) {

        try (InputStream docxInputStream = new FileInputStream(file);
             ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream()) {

            converter.convert(docxInputStream).as(DocumentType.DOCX)
                    .to(pdfOutputStream).as(DocumentType.PDF).execute();

            byte[] pdfBytes = pdfOutputStream.toByteArray();
            return getPageNumbersFromPdf(pdfBytes, clbmlVoList);

        } catch (IOException e) {
            throw new RuntimeException("读取DOCX文件或从DOCX转换为PDF时失败", e);
        }
    }


    public void shutDown() {
        if (converter != null) {
            converter.shutDown();
        }
    }


    /**
     * 绑定目录所对应的页数
     *
     * @param pdfBytes
     * @param clbmlVoList
     * @return
     * @throws IOException
     */
    public static List<ClbmlVo> getPageNumbersFromPdf(byte[] pdfBytes, List<ClbmlVo> clbmlVoList) throws IOException {

//        List<ClbmlVo> returnList=new ArrayList<>();
        try (PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes))) {
            PDFTextStripper stripper = new PDFTextStripper();
            int totalPages = document.getNumberOfPages();
            //循环pdf文件页数
            for (int i = 0; i < totalPages; i++) {
                stripper.setStartPage(i + 1);
                stripper.setEndPage(i + 1);
                //获取每页内容转成字符串
                String pageContent = stripper.getText(document);
               //循环目录对象，获取匹配页码
                for (int j = 0; j < clbmlVoList.size(); j++) {
                    ClbmlVo clbmlVo = clbmlVoList.get(j);
                   // 通过匹配页面内容，将匹配的 ClbmlVo 对象的页数设置为当前页码 利用矿区编号和矿产名称
                        if ((pageContent.contains(clbmlVo.getKqbh()) &&
//                                pageContent.contains(clbmlVo.getKczh()) &&
                                pageContent.contains("矿种:"+clbmlVo.getKcmc()))) {
                            clbmlVo.setPageNum(i + 1);
                            clbmlVo.setKsxx(clbmlVo.getKsxx() + clbmlVo.getKqbh());
//                            returnList.add(clbmlVo);

                    }
                }
            }
        }
//        /
//        returnList.sort(Comparator.comparingInt(ClbmlVo::getPageNum));
        return clbmlVoList;
    }


    /**
     * 将文件列表压缩到指定的 ZIP 文件中
     *
     * @param filePaths   要压缩的文件路径列表
     * @param zipFilePath 压缩后的 ZIP 文件路径
     * @throws IOException 如果压缩过程中发生错误
     */
    public static void zipFiles(List<String> filePaths, String zipFilePath) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(zipFilePath);
             ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos))) {

            for (String filePath : filePaths) {
                Path file = Paths.get(filePath);
                if (Files.exists(file)) {
                    ZipEntry zipEntry = new ZipEntry(file.getFileName().toString());
                    zos.putNextEntry(zipEntry);

                    Files.copy(file, zos);
                    zos.closeEntry();
                } else {
                    System.out.println("文件不存在: " + filePath);
                }
            }
        }
    }


    /**
     * 匹配以字母开头的单位片段，后面跟着数字2或3，且不被字母包围（排除化学式）
     * 如：cm2 → 替换； H2O → 不匹配
     *
     * @param input
     * @return
     */
    public String formatUnit(String input) {
        if (input == null || input.isEmpty()) {
            return input;
        }

        Matcher matcher = UNIT_EXP_PATTERN.matcher(input);
        StringBuffer result = new StringBuffer();

        while (matcher.find()) {
            String base = matcher.group(1);
            char digit = matcher.group(2).charAt(0);

            Character superScript = SUPERSCRIPT_MAP.get(digit);
            if (superScript != null) {
                matcher.appendReplacement(result, base + superScript);
            } else {
                matcher.appendReplacement(result, matcher.group());
            }
        }
        matcher.appendTail(result);

        return result.toString();
    }


}

